//
//  ViewController.swift
//  JKVideoCapture
//
//  Created by 王冲 on 2020/8/3.
//  Copyright © 2020 王冲. All rights reserved.
//

import UIKit
import AVFoundation
class ViewController: UIViewController {

    fileprivate lazy var session: AVCaptureSession = AVCaptureSession()
    fileprivate var videoOutput: AVCaptureVideoDataOutput?
    /// 预览层
    fileprivate var previewLayer: AVCaptureVideoPreviewLayer?
    /// 视频的输入
    fileprivate var videoInput: AVCaptureDeviceInput?
    /// 录制文件的对象
    fileprivate lazy var movieOutput: AVCaptureMovieFileOutput = AVCaptureMovieFileOutput()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        print("路径：\(NSHomeDirectory())")
        // 1.初始化视频的输入 & 输出
        setUpVideoInputAndOutput()
        // 2.初始化音频的输入 & 输出
        setUpAudioInputAndOutput()
        // 3.初始化一个预览图层
        setUpPreviewLayer()
    }
}

extension ViewController {
    
    /// 1.初始化视频的输入 & 输出
    func setUpVideoInputAndOutput() {
        // 1.1、添加视频的输入
        /**
          position：摄像头的选择：front(前置)、back(后置)
         */
        guard let deveices = AVCaptureDevice.devices() as? [AVCaptureDevice] else { return }
        // 前置摄像头
        guard let device = deveices.filter({ $0.position == .front }).first else { return }
        // 输入
        guard let input = try? AVCaptureDeviceInput(device: device) else { return }
        videoInput = input
        // 1.2、视频的输出
        let output = AVCaptureVideoDataOutput()
        // 并发全局队列，防止阻塞主线程
        output.setSampleBufferDelegate(self, queue: DispatchQueue.global())
        self.videoOutput = output
        // 1.3、添加输入和输出
        addInputOutputToSession(input: input, output: output)
    }
    
    /// 2.初始化音频的输入 & 输出
    func setUpAudioInputAndOutput() {
        // 2.1、输入
        guard let device = AVCaptureDevice.default(for: AVMediaType.audio) else {
            return
        }
        guard let input = try? AVCaptureDeviceInput(device: device) else {
            return
        }
        // 2.2、输出
        let output = AVCaptureAudioDataOutput()
        output.setSampleBufferDelegate(self, queue: DispatchQueue.global())
        // 2.3、添加输入和输出
        addInputOutputToSession(input: input, output: output)
    }
    
    private func addInputOutputToSession(input: AVCaptureInput, output: AVCaptureOutput) {
        session.beginConfiguration()
        if session.canAddInput(input) {
            session.addInput(input)
        }
        if session.canAddOutput(output) {
            session.addOutput(output)
        }
        session.commitConfiguration()
    }
    
    /// 3.初始化一个预览图层
    func setUpPreviewLayer() {
        // 3.1、创建预览图层
        let previewLayer = AVCaptureVideoPreviewLayer(session: session)
        // 3.2、设置 previewLayer 属性
        previewLayer.frame = CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: self.view.bounds.size.height)
        // 3.3、将图层添加到控制器view的 layer 中
        view.layer.insertSublayer(previewLayer, at: 0)
        self.previewLayer = previewLayer
    }
}

extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate {
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        // 采集到画面 一帧一帧的数据
        // 编码是对 sampleBuffer 每一帧的数据进行编码
        guard let weakVideoOutput = videoOutput else {
            return
        }
        if weakVideoOutput.connection(with: AVMediaType.video) == connection {
            print("采集到视频数据")
        } else {
            print("采集到------音频数据")
        }
    }
}

extension ViewController {
    
    /// 开启视频的录制
    func setupMovieFileOutput() {
        // 1.创建写入文件的输出
        let fileOutput = AVCaptureMovieFileOutput()
        // 保存录制的输出对象
        movieOutput = fileOutput
        /*
        guard let connection = fileOutput.connection(with: AVMediaType.video) else {
            return
        }
        connection.automaticallyAdjustsVideoMirroring = true
        */

        if session.canAddOutput(fileOutput) {
            session.addOutput(fileOutput)
        }
        
        // 2.直接开始写入文件
        let filePath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! + "/abc.mp4"
        let fileUrl = URL(fileURLWithPath: filePath)
        // 3.开始写入
        fileOutput.startRecording(to: fileUrl, recordingDelegate: self)
    }
}

extension ViewController: AVCaptureFileOutputRecordingDelegate {
    
    func fileOutput(_ output: AVCaptureFileOutput, didStartRecordingTo fileURL: URL, from connections: [AVCaptureConnection]) {
        print("开始写入文件")
    }
    func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
        print("完成写入文件")
    }
    
    
}

// MARK:- 对采集的控制器的方法
extension ViewController {
    ///  开始采集
    /// - Parameter sender: sender description
    @IBAction func starCapture(_ sender: Any) {
        session.startRunning()
        // 采集的同时进行视频的录制
        setupMovieFileOutput()
    }
    
    /// 停止采集
    /// - Parameter sender: sender description
    @IBAction func stopCapture(_ sender: Any) {
        // 停止录制
        movieOutput.stopRecording()
        
        // 1.停止采集
        session.stopRunning()
        // 2.移除预览层
        previewLayer?.removeFromSuperlayer()
        // 3.将对象重置为nil

    }
    
    /// 切换相机
    /// - Parameter sender: sender description
    @IBAction func rotateCamera(_ sender: Any) {
        //1、取出之前镜头的方向
        guard let weakVideoInput = videoInput else {
            return
        }
        let postion: AVCaptureDevice.Position = weakVideoInput.device.position == AVCaptureDevice.Position.front ? AVCaptureDevice.Position.back : AVCaptureDevice.Position.front
        
        guard let deveices = AVCaptureDevice.devices() as? [AVCaptureDevice] else { return }
        // 前置摄像头
        guard let device = deveices.filter({ $0.position == postion }).first else { return }
        // 输入
        guard let newInput = try? AVCaptureDeviceInput(device: device) else { return }
        
        // 保持最新的输入
        videoInput = newInput
        
        // 2.移除之前的 input，添加新的 input
        session.beginConfiguration()
        session.removeInput(weakVideoInput)
        if session.canAddInput(newInput) {
            session.addInput(newInput)
        }
        session.commitConfiguration()
    }
}

